Keep Neo4j credentials out of query failure messages - #1314
Open
malberts wants to merge 1 commit into
Open
Conversation
The driver reports an unreachable server by quoting the Bolt URI it dialed, credentials included. Neo4jQueryService rethrew that message verbatim as BackendUnavailableException, and the query surfaces hand it straight to the caller: CypherQueryApi returns it in the 503 body, on a route the neowiki-query right leaves open to anonymous users by default. Reproduced against an unreachable server, an unauthenticated POST to /rest.php/neowiki/v0/query/cypher returned the configured Neo4j password. Route both backend-failure paths through BackendFailureMessage, as the ten other places that surface a backend message already do. The message keeps naming the server it could not reach, so it stays as useful as before. Community Edition installs cannot separate the read user from the write user, so the read URL that queries use commonly carries the wiki's only Neo4j credentials. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
malberts
marked this pull request as ready for review
August 23, 2026 08:00
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Neo4j driver reports an unreachable server by quoting the Bolt URI it dialed, credentials
included.
Neo4jQueryServicerethrew that message verbatim asBackendUnavailableException, andthe query surfaces pass it straight to the caller:
CypherQueryApiputs it in the 503 body, on aroute
neowiki-queryleaves open to anonymous users by default.Against a wiki pointed at an unreachable Neo4j, an unauthenticated request returns the password:
Both backend-failure paths now go through
BackendFailureMessage::withoutCredentials, which theten other places that surface a backend message already use. The same request then answers:
{"errorType":"backendUnavailable","message":"Cannot connect to any server on alias: default with Uris: ('bolt://graph.example:7687')"}The message still names the server it could not reach, so it stays as useful for diagnosis. Fixing
it in the service rather than in the REST handler covers the other two query surfaces,
{{#cypher_raw}}andnw.query(), which take the same message from the same place.#1258 extends the same helper to three further surfaces and is open in parallel. It leaves the
query path alone and this leaves its three alone, so the two do not overlap and can land in either
order.
Why now
The exposure is old, but it used to be bounded: the backend was a health-gated container on the
compose network, holding a password the stack generated for itself. #1308 made the Neo4j address
configurable, so a remote or managed server is now a supported target, and a remote server is one
that has maintenance windows and expiring certificates. An unreachable backend went from a local
misconfiguration to a routine operating condition.
Worth noting for severity: separate read and write Neo4j users need Enterprise Edition, and
docs/operations/installation.mdtells Community Edition installs to point both URLs at the sameuser. On those installs the read URL that queries use carries the wiki's only Neo4j credentials.
Considered, omitted
CypherQueryApiinstead. It would leave{{#cypher_raw}}andnw.query()leaking.translateNeo4jException. Those messages come fromgetNeo4jMessage(), whichreports what the query did wrong rather than what was dialed, so they carry no URI.
Testing
The two new tests fail on master with the password in the assertion output, and pass with the fix.
Beyond that the behaviour was reproduced end to end on a live stack, pointing the wiki at an
unreachable host with a canary password: anonymous POST returns the canary before the change, and
the host without it after. Full PHPUnit, PHPCS and PHPStan are green, with the suite matching
master's baseline.
The existing
CypherQueryApiTeststubs a benign message, which is why CI never caught this; the newtests use a message shaped like the driver's real one.